package com.xishu.service;

import com.xishu.aop.Validator;
import com.xishu.client.EmailClient;
import com.xishu.config.Config;
import com.xishu.config.Constant;
import com.xishu.dao.jpa.UserDao;
import com.xishu.entity.Company;
import com.xishu.entity.User;
import com.xishu.entity.plat.GlobalSetting;
import com.xishu.entity.plat.PrivilegeMapping;
import com.xishu.entity.plat.UserCompanyMapping;
import com.xishu.entity.plat.UserShopMapping;
import com.xishu.entity.shop.Shop;
import com.xishu.exception.ResponseStatusException;
import com.xishu.response.ResponseData;
import com.xishu.response.ResponseStatus;
import com.xishu.response.VerifyUtil;
import com.xishu.util.*;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.xishu.config.Constant.COMPANY_PRIVILEGE_UPDATE_TIME;
import static com.xishu.response.ResponseStatus.PASSWORD_WRONG_REACH_MAX_TIME;

public class UserService implements Tools {
    private static UserService instance = new UserService();
    private static Logger logger = LoggerFactory.getLogger(UserService.class);

    private UserService() {
    }

    public static UserService getInstance() {
        return instance;
    }

    private static CommonService commonService = CommonService.getInstance();
    private static GlobalService globalService = GlobalService.getInstance();

    /**
     * 查询用户
     *
     * @param userId
     * @return
     */
    public User findUser(Long userId) throws Exception {
        return commonService.findEntity(userId, User.class);
    }

    /**
     * 通过ID列表来查询所有的用户
     *
     * @param userIds
     * @return
     */
    public List<User> findUserByIds(List<Long> userIds) {
        User user = new User();
        user.setIds(userIds);
        return commonService.searchAll(user);
    }

    /**
     * 登录
     *
     * @param user
     * @return
     */
    public User login(User user) {
        if (isEmpty(user.getName())) {
            logger.info("user name is empty");
            return null;
        }

        user.setName(user.getName().trim());
        user.setName(user.getName().toLowerCase());
        User req = new User();
        req.setName(user.getName().toLowerCase());
        // TODO: 2020/12/10 没有记录操作日志
        User userInDb = findUserByName(user.getName());

        if (userInDb == null) {
            logger.info("can not find any user");
            return null;
        }

        //已经删除的用户不能再登录
        if (getBoolean(userInDb.flagDelete)) {
            logger.info("user deleted, userId is {}", userInDb.getId());
            return null;
        }

        logger.info("user size is {}", user.toString());

        String sha256 = Sha256Util.sha256(user.getPassword(), userInDb.getSalt());

        if (StringUtils.equals(sha256, userInDb.getPassword())) {
            commonService.dealFieldMapping(userInDb, false);
            return userInDb;
        } else {
            return null;
        }
    }

    /**
     * 清除用户敏感信息
     *
     * @param user
     */
    public void cleanUser(User user) {
        user.setPassword("");
        user.setSalt("");
        user.setSecondaryPassword("");
        user.setSecondarySalt("");
    }

    /**
     * 创建用户
     *
     * @param user
     */
    public void createUser(User user) {
        String salt = Sha256Util.generateSalt();
        user.setSalt(salt);
        user.setPassword(Sha256Util.sha256(user.getPassword(), user.getSalt()));
    }

    /**
     * 判断是否是超级管理员用户
     *
     * @param user
     * @return
     */
    public boolean isAdmin(User user) {
        if (user == null) {
            return false;
        }

        return getBoolean(user.getAdmin());
    }

    /**
     * 通过邮件查询账号
     *
     * @param email
     * @return
     */
    public User findUserByEmail(String email) throws ResponseStatusException {
        VerifyUtil.isEmpty(email);

        User user = new User();
        user.setEmail(email);
        user.ignoreLike = true;

        List<User> userList = commonService.findLimit(user, 10);
        if (isEmpty(userList)) {
            return null;
        }

        return userList.get(0);
    }

    /**
     * 通过邮件或者姓名查询账号
     *
     * @param email
     * @return
     */
    public User findUserByEmailOrName(String email) throws Exception {
        User user = findUserByEmail(email);
        if (user != null) {
            return user;
        }

        return findUserByName(email);
    }

    /**
     * 通过名称来查询账号
     *
     * @param name
     * @return
     */
    public User findUserByName(String name) {
        User user = new User();
        user.setName(name);
        user.ignoreLike = true;

        List<User> userList = commonService.findLimit(user, 10);
        if (isEmpty(userList)) {
            return null;
        }

        return userList.get(0);
    }

    /**
     * 更新用户信息
     *
     * @param reqUser
     * @return
     * @throws Exception
     */
    public ResponseData updateUser(User reqUser) throws Exception {
        //如果传了二级密码，校验二级密码的格式
        if (isNotEmpty(reqUser.getSecondaryPassword())) {
            boolean sixNumber = PatternUtil.isSixNumber(reqUser.getSecondaryPassword());
            VerifyUtil.verify(() -> sixNumber, ResponseStatus.SIX_NUMBER);
        }

        clearDefaultPassword(reqUser);

        //如果传了密码，修改密码
        if (isNotEmpty(reqUser.getPassword())) {
            String salt = Sha256Util.generateSalt();
            reqUser.setSalt(salt);
            reqUser.setPassword(Sha256Util.sha256(reqUser.getPassword(), salt));
        }

        //如果传了二级密码，修改二级密码
        if (isNotEmpty(reqUser.getSecondaryPassword())) {
            VerifyUtil.verify(() -> Pattern.matches("\\d{6}", reqUser.getSecondaryPassword()), ResponseStatus.SIX_NUMBER);
            String salt = Sha256Util.generateSalt();
            reqUser.setSecondarySalt(salt);
            reqUser.setSecondaryPassword(Sha256Util.sha256(reqUser.getSecondaryPassword(), salt));
        }

        return commonService.updateObject(reqUser);
    }

    /**
     * 获取显示的用户名
     *
     * @param user
     * @return
     */
    public String getShowUserName(User user) {
        if (user == null) {
            return "";
        }

        if (isNotEmpty(user.getNickName())) {
            return user.getNickName();
        }

        return user.getName();
    }

    /**
     * 如果找得到餐厅公司，那么用餐厅公司的名字
     * 如果没有，那么用账号名称
     *
     * @param user
     * @return
     */
    public String getUserCompanyName(User user) throws Exception {
        UserCompanyMapping userCompanyMapping = new UserCompanyMapping();
        userCompanyMapping.setUserId(user.getId());
        Optional<UserCompanyMapping> userCompanyMappingOptional = commonService.searchOneOptional(userCompanyMapping);

        if (userCompanyMappingOptional.isPresent()) {
            Long companyId = userCompanyMappingOptional.get().getCompanyId();

            logger.info("company id is {}", companyId);
            if (companyId != null) {
                Company company = commonService.findEntity(companyId, Company.class);
                return company.getName();
            }
        }

        return getShowUserName(user);
    }

    /**
     * 通过账号ID来查询公司的ID
     *
     * @param userId
     * @return
     */
    public Long findCompanyIdByUserId(Long userId) throws Exception {
        User user = findUser(userId);

        if (user == null) {
            return null;
        }

        if (user.getCompanyId() != null) {
            return user.getCompanyId();
        }

        UserCompanyMapping userCompanyMapping = new UserCompanyMapping();
        userCompanyMapping.setUserId(userId);
        userCompanyMapping.setEntity(true);
        List<UserCompanyMapping> userCompanyMappingList = commonService.searchAll(userCompanyMapping);
        if (isEmpty(userCompanyMappingList)) {
            return null;
        }

        return userCompanyMappingList.get(0).getCompanyId();
    }

    /**
     * 判断用户是否有操作该分店的权限
     *
     * @param shopId
     * @return
     */
    public boolean hasShopPrivilege(Long shopId) {
        try {
            User user = ServletUtil.getUser();
            return hasShopPrivilege(user, shopId);
        } catch (Exception e) {
            logger.error("e", e);
            return false;
        }
    }


    /**
     * 判断用户是否有操作该分店的权限
     *
     * @param user
     * @param shopId
     */
    public boolean hasShopPrivilege(User user, Long shopId) {
        try {
            if (user == null) {
                logger.info("user is null");
                return false;
            }

            if (shopId == null) {
                logger.info("shopId is null");
                return false;
            }

            if (getBoolean(user.getAdmin())) {
                logger.info("admin user");
                return true;
            }

            UserShopMapping userShopMapping = MappingService.getInstance().findUserShopMapping(user.getId(), shopId);
            boolean bindShop = userShopMapping != null;

            //如果已经绑定了商店，可以直接返回
            if (bindShop) {
                return true;
            }

            //没有绑定商店，那么查看是否绑定了公司
            Shop shop = ShopService.getInstance().findShopById(shopId);
            UserCompanyMapping companyMapping = MappingService.getInstance().findUserCompanyMapping(user.getId(), shop.getCompanyId());

            return companyMapping != null;
        } catch (Exception e) {
            logger.error("e", e);
            return false;
        }
    }

    /**
     * 邀请指定账号成为店铺经理
     *
     * @param email
     * @param shopIdList
     * @param shopPrivilegeList
     * @param lang
     * @throws Exception
     */
    public void inviteManager(String email, List<Long> shopIdList, List<String> shopPrivilegeList, String lang) throws Exception {
        GlobalSetting globalSetting = globalService.getGlobalSetting();
        String address = globalSetting.getAddress();
        String city = globalSetting.getCity();
        String companyWebsite = globalSetting.getCompanyWebsite();
        String emailAddress = globalSetting.getEmailAddress();
        shopPrivilegeList = getList(shopPrivilegeList);

        List<Shop> shopList = shopIdList.stream().map(shopId -> {
            try {
                return ShopService.getInstance().findShopById(shopId);
            } catch (Exception e) {
                logger.error("e", e);
                return null;
            }
        }).collect(Collectors.toList());

        Company company = commonService.findEntity(shopList.get(0).getCompanyId(), Company.class);
        String inviteUserName = UserService.getInstance().getShowUserName(ServletUtil.getUser());

        //加密字符串
        String emailCode = MongoId.generateId();
        JRedisUtil.setKV(emailCode, email + "#" + StringUtils.join(shopIdList, ",") + "#" + StringUtils.join(shopPrivilegeList, "|"));
        JRedisUtil.expire(emailCode, 30 * 60);
        ShopService shopService = ShopService.getInstance();
        List<String> shopNames_zh = shopList.stream().map(s -> "[ " + shopService.getShopNameZh(s) + " ]").collect(Collectors.toList());
        List<String> shopNames_en = shopList.stream().map(s -> "[ " + shopService.getShopNameEn(s) + " ]").collect(Collectors.toList());

        String showShopNameZhList = StringUtils.join(shopNames_zh, ",");
        String showShopNameEnList = StringUtils.join(shopNames_en, ",");

        //判断账号是否唯一
        UserDao userDao = BeanUtil.getBean(UserDao.class);
        List<User> userList = userDao.findListByEmail(email);
        VerifyUtil.verify(() -> isEmpty(userList), ResponseStatus.USER_EXIST);

        String validAddress = Config.getInstance().getEndPoint() + "/invite/manager/" + AES.encrypt(emailCode);
        logger.info("inviteAddress is {}", validAddress);

        String showUrl = Config.getInstance().getEndPoint() + "/manage/index.html#/yqjl?code=" + AES.encrypt(emailCode);
        //设置当前邮件的最近的邀请CODE
        JRedisUtil.setKV(String.format("%s.current.invite.code", email), emailCode);
        JRedisUtil.expire(String.format("%s.current.invite.code", email), 30 * 60);

        String title = "Welcome to DoLA - Please Verify Your Email. 歡迎使用DoLA – 請驗證您的電郵。";
        String logoUrl = PlatService.getInstance().getLogoUrl();

        String content = String.format("<!DOCTYPE html>\n" +
                "<html lang=\"en\">\n" +
                "<head>\n" +
                "    <meta charset=\"UTF-8\">\n" +
                "    <title>Title</title>\n" +
                "</head>\n" +
                "<body>\n" +
                "<div style=\"padding-right: 10%%\" align=\"right\"><img style=\"width: 100px;height: 100px;\" src=\"%s\"></div>\n" +
                "<div style=\"padding-left: 10%%\">\n" +
                "    <div>Hi %s,</div>\n" +
                "    <br/>\n" +
                "    <div>\n" +
                "        %s has invited you as the manager of %s %s。To finish your account setup, please verify your email by clicking\n" +
                "        the button below:\n" +
                "    </div>\n" +
                "    <br/>\n" +
                "    <div>\n" +
                "        您好 %s，\n" +
                "    </div>\n" +
                "    <div>\n" +
                "        %s 邀請你成爲%s %s的POS經理。爲了完成您的賬戶注冊，请點擊以下按鈕來驗證您的電郵：\n" +
                "    </div>\n" +
                "    <br/>\n" +
                "    <br/>\n" +
                "    <div align=\"center\">\n" +
                "        <div style=\"width: 150px;height: 90px;text-align: center;\">\n" +
                "            <div>\n" +
                "                <a href=\"%s\" style=\"text-decoration:none; \">\n" +
                "                    <div style=\"width: 100%%;height: 100%%;background-color: #4793A3;border-width: 0px; border-radius:15px; cursor: pointer\">\n" +
                "                        <div style=\"padding-top:10%%;padding-bottom: 10%%;color: white\">Verify your email</div>\n" +
                "                        <div style=\"padding-bottom: 10%%;color: white\">點擊驗證</div>\n" +
                "                    </div>\n" +
                "                </a>\n" +
                "            </div>\n" +
                "        </div>\n" +
                "    </div>\n" +
                "    <br/>\n" +
                "    <br/>\n" +
                "\n" +
                "    <div>If you are not %s, please ignore this email. 如果您不是%s，請忽略這封電郵。</div>\n" +
                "\n" +
                "    <br/>\n" +
                "    <br/>\n" +
                "    <div align=\"center\">\n" +
                "        <div style=\"color: #808080\">" + address + "<br/>\n" +
                "          " + city + "<br/>\n" +
                "        </div>\n" +
                "        <div style=\"color: #4793A3;text-decoration: none !important;\">" + emailAddress + " |\n" +
                "            " + companyWebsite + "\n" +
                "        </div>\n" +
                "    </div>\n" +
                "</div>\n" +
                "</body>\n" +
                "</html>\n", logoUrl, email, inviteUserName, company.getName(), showShopNameEnList, email, inviteUserName, company.getName(), showShopNameZhList, showUrl, email, email);


        logger.info("content is {}", content);
        EmailClient.getInstance().sendEmail(email, title, content);
    }

    /**
     * 校验输入的密码是否正确
     *
     * @param userInDb
     * @param password
     * @return
     */
    public Boolean isPasswordOk(User userInDb, String password) {
        if (isEmpty(password)) {
            logger.info("password is empty");
            return false;
        }

        if (userInDb == null) {
            logger.info("user is empty");
            return false;
        }

        if (isEmpty(userInDb.getSalt())) {
            logger.info("salt is empty");
            return false;
        }

        String passwordInDb = userInDb.getPassword();
        String comparePassword = Sha256Util.sha256(password, userInDb.getSalt());
        return equals(comparePassword, passwordInDb);
    }

    /**
     * 校验输入的二级密码是否正确
     *
     * @param userInDb
     * @param secondaryPassword
     * @return
     */
    public boolean isSecondaryPasswordOk(User userInDb, String secondaryPassword) {
        if (isEmpty(secondaryPassword)) {
            logger.info("password is empty");
            return false;
        }

        if (userInDb == null) {
            logger.info("user is empty");
            return false;
        }

        if (isEmpty(userInDb.getSecondarySalt())) {
            logger.info("salt is empty");
            return false;
        }

        String userSecondaryPassword = userInDb.getSecondaryPassword();
        String comparePassword = Sha256Util.sha256(secondaryPassword, userInDb.getSecondarySalt());
        return equals(comparePassword, userSecondaryPassword);

    }

    /**
     * 如果传过来的是默认密码，则认为客户端没有做处理
     *
     * @param user
     */
    public void clearDefaultPassword(User user) throws ResponseStatusException {
        if (user == null) {
            return;
        }

        if (equals(user.getPassword(), Constant.USER_DEFAULT_PASSWORD)) {
            user.setPassword(null);
        }

        if (equals(user.getSecondaryPassword(), Constant.USER_DEFAULT_PASSWORD)) {
            user.setSecondaryPassword(null);
        }
    }

    /**
     * 查询默认的权限配置
     *
     * @param companyId
     */
    public Optional<PrivilegeMapping> findDefaultMappingPrivilege(Long companyId) {
        if (companyId == null) {
            return Optional.empty();
        }

        PrivilegeMapping privilegeMapping = new PrivilegeMapping();
        privilegeMapping.setCompanyId(companyId);
        privilegeMapping.setDefaultPrivilege(true);
        return commonService.searchOneOptional(privilegeMapping);
    }

    /**
     * 查询一个分店下面的最有的餐厅经理账号
     *
     * @param companyId
     * @return
     */
    public List<User> findUserListByCompnayId(Long companyId) {
        User user = new User();
        user.setCompanyId(companyId);
        user.setEntity(true);
        List<User> userList = commonService.searchAll(user);

        return userList.stream().filter(p -> getBoolean(p.getShopManager()) || getBoolean(p.getCompanyManager())).collect(Collectors.toList());
    }

    /**
     * 添加用户登录失败的次数
     *
     * @param name
     */
    public void addUserLoginFailedTime(String name) throws Exception {
        if (isEmpty(name)) {
            return;
        }

        User user = findUserByName(name);
        if (user != null) {
            user.setFailedTime(getInt(user.getFailedTime()) + 1);
            commonService.save(user);

            //如果添加次数超过了,直接报错
            if (user.getFailedTime() >= Config.getInstance().passwordMaxWrongNumber()) {
                addUserExpireTime(user.getId());
                VerifyUtil.throwError(PASSWORD_WRONG_REACH_MAX_TIME);
            }
        }
    }

    /**
     * 添加用户的失效时间
     *
     * @param userId
     * @throws Exception
     */
    public void addUserExpireTime(Long userId) throws Exception {
        Long companyId = UserService.getInstance().findCompanyIdByUserId(userId);

        if (companyId != null) {
            JRedisUtil.setKV(COMPANY_PRIVILEGE_UPDATE_TIME + companyId, System.currentTimeMillis());
            //设置一个小时超时
            JRedisUtil.expire(COMPANY_PRIVILEGE_UPDATE_TIME + companyId, 60 * 60);
        } else {
            logger.info("can not find the company id for user {}", userId);
        }
    }

    /**
     * 如果账号的商铺权限不对，那么不修改
     *
     * @param reqUser
     */
    public void resetShopPrivilege(User reqUser) {
        List<String> shopPrivilegeList = reqUser.getShopPrivilegeList();
        if (shopPrivilegeList == null) {
            return;
        }

        //忽略掉空值，空字符串
        shopPrivilegeList = shopPrivilegeList.stream().filter(p -> p != null && !"null".equalsIgnoreCase(p)).collect(Collectors.toList());

        if (shopPrivilegeList.isEmpty()) {
            reqUser.setShopPrivilegeList(null);
        }
    }

    /**
     * 删除这个分店下面的服务员
     *
     * @param shopId
     */
    public void deleteWater(Long shopId) throws Exception {
        User reqUser = new User();
        reqUser.setShopId(shopId);
        List<User> userList = commonService.searchAll(reqUser);

        for (User user : userList) {
            if (new Validator().isShopManager(user)) {
                continue;
            }

            //这里肯定是服务员了，需要删除
            commonService.delete(user, false, null);
        }
    }

    /**
     * 获取用户的邮箱地址
     *
     * @param user
     * @return
     */
    public String userEmail(User user) {
        if (user == null) {
            return null;
        }

        //用户邮箱
        if (isNotEmpty(user.getEmail())) {
            return user.getEmail();
        }

        //用户名满足邮件名的格式
        if (PatternUtil.isEmail(user.getName())) {
            return user.getName();
        }

        return null;
    }

    /**
     * 判断用户是否还存在
     *
     * @param userId
     * @return
     */
    public boolean isUserExist(Long userId) throws Exception {
        Optional<User> userOptional = commonService.searchOneOptional(userId, User.class);
        if (!userOptional.isPresent()) {
            return false;
        }

        User user = userOptional.get();
        boolean delete = getBoolean(user.getFlagDelete());
        return !delete;
    }

    /**
     * 判断账号是否已经存在了
     *
     * @param email
     * @return
     */
    public boolean isUserExist(String email) {
        UserDao userDao = BeanUtil.getBean(UserDao.class);
        List<User> userList = userDao.findListByEmail(email);
        return isNotEmpty(userList);
    }

    /**
     * 删除公司下面的所有账号
     *
     * @param company
     */
    public void deleteUserInCompany(Company company) {
        Long companyId = company.getId();
        List<UserCompanyMapping> userCompanyMapping = MappingService.getInstance().findUserCompanyMapping(companyId);

        //将餐厅账号标记成删除
        userCompanyMapping.forEach(mapping -> {
            Long userId = mapping.getUserId();
            try {
                User user = findUser(userId);
                if (getBoolean(user.getCompanyManager()) || getBoolean(user.getShopManager())) {
                    user.setFlagDelete(true);
                    commonService.save(user);
                }
            } catch (Exception e) {
                logger.error("e", e);
            }
        });

        //将分店账号标记成删除
        List<Shop> shopList = ShopService.getInstance().findShopListByCompanyId(companyId);
        shopList.forEach(shop -> {
            try {
                List<UserShopMapping> mappingList = MappingService.getInstance().findUserShopMapping(shop.getId());
                mappingList.forEach(mapping -> {
                    Long userId = mapping.getUserId();
                    try {
                        User user = findUser(userId);
                        if (getBoolean(user.getCompanyManager()) || getBoolean(user.getShopManager())) {
                            user.setFlagDelete(true);
                            commonService.save(user);
                        }
                    } catch (Exception e) {
                        logger.error("e", e);
                    }
                });
            } catch (Exception e) {
                logger.error("e");
            }
        });
    }

    /**
     * 在现有权限基础上，再添加POS端的权限
     *
     * @param newPrivilegeList
     * @param oldPrivilegeList
     * @return
     */
    public List<String> addPosPrivilege(List<String> newPrivilegeList, List<String> oldPrivilegeList) {
        List<String> privilegeList = new ArrayList<>();
        privilegeList.addAll(newPrivilegeList);

        if (oldPrivilegeList.contains("sjzhgl_bdckdpzt")) {
            privilegeList.add("sjzhgl_bdckdpzt");
        }

        if (oldPrivilegeList.contains("sjzhgl_bddpztmmbh")) {
            privilegeList.add("sjzhgl_bddpztmmbh");
        }

        return privilegeList;
    }



    //查询餐厅公司对应的账号
    public User findCompanyUser(Long companyId) {

        UserCompanyMapping userCompanyMapping = new UserCompanyMapping();
        userCompanyMapping.setCompanyId(companyId);

        List<UserCompanyMapping> userCompanyMappingList = commonService.searchAll(userCompanyMapping);

        if (isEmpty(userCompanyMappingList)) {
            return null;
        }
        User user = new User();

        List<Long> userIds = userCompanyMappingList.stream().map(p -> p.getUserId()).collect(Collectors.toList());
        user.setIds(userIds);
        List<User> users = commonService.searchAll(user);

        Optional<User> userOption = users.stream().filter(p -> getBoolean(p.getCompanyManager())).findFirst();
        if (userOption.isPresent()) {
            User companyUser = userOption.get();
            return companyUser;
        } else {
            return null;
        }
    }

}
